#include "../Misc/part2-3_shared.h"

#define NUMBER_OF_ENTRIES 65536
#define NUMBER_OF_PAIRS_PER_BLOCK 10
#define SEED (0x1100220033004400)

int main(int argc, char **argv) {

	DEHT *deht;
	LONG_INDEX_PROJ length, i = 0, rand;
	char password[MAX_PASSWORD_LENGTH];
	RULE rule;
	char **lexicon = NULL;
	BasicHashFunctionPtr hash_function;
	unsigned char binary_hashed_passowrd[MAX_HASH_SIZE];
	int hash_func_size_in_bytes, count;
	int status = 0 , number_of_passwords_to_generate;

	if (argc != 6) {
		fprintf(stderr,
				"Error: Usage exhaustive_table_generator <rule> <lexicon> <hash> <filenames_prefix> <\"all\" or number of random password>\n ");
		exit(1);
	}

	if (strcmp("all",argv[5])) {
		while(isdigit(argv[5][i++]));
		if (i < strlen(argv[5])) {
			fprintf(stderr,
				"Error: Usage exhaustive_table_generator <rule> <lexicon> <hash> <filenames_prefix> <\"all\" or number of random password>\n ");
			exit(1);
		}
		number_of_passwords_to_generate = atoi(argv[5]);
	}


	if (!strcmp(argv[3], "MD5")) {
		hash_function = MD5BasicHash;
		hash_func_size_in_bytes = MD5_OUTPUT_LENGTH_IN_BYTES;
	} else if (!strcmp(argv[3], "SHA1")) {
		hash_function = SHA1BasicHash;
		hash_func_size_in_bytes = SHA1_OUTPUT_LENGTH_IN_BYTES;
	} else {
		fprintf(stderr, "Error: Hash %s is not supported\n", argv[3]);
		exit(1);
	}

	lexicon = load_lexicon(argv[2],&length); /* if something went wrong, this will exit the program */
	status = create_rule(&rule,argv[1],lexicon,length);
	if (status == -1) {
		free_lexicon(lexicon,length);
		exit(1);
	}

	deht = create_empty_DEHT(argv[4], cryptHashToTable, cryptHashTo64bit, argv[3],
			NUMBER_OF_ENTRIES, NUMBER_OF_PAIRS_PER_BLOCK, BYTES_PER_VALIDATION_KEY);
	if (deht == NULL) {
		free_lexicon(lexicon,length);
		free_rule(&rule);
		exit(1);
	}

	status = calc_DEHT_last_block_per_bucket(deht);

	if (status == DEHT_STATUS_FAIL) {
		free_lexicon(lexicon,length);
		free_rule(&rule);
		lock_DEHT_files(deht);
	}

	if (!strcmp("all",argv[5])) {
		for (i = 0 ; i < rule.number_of_possible_passwords ; i++) {
			generate_kth_password(i,password,rule);
			cryptHash(hash_function,password,binary_hashed_passowrd);
			status = add_DEHT(deht,binary_hashed_passowrd,hash_func_size_in_bytes,(unsigned char *)password,strlen(password));
			if (status == DEHT_STATUS_FAIL) {
				free_lexicon(lexicon,length);
				free_rule(&rule);
				erase_DEHT_file(deht);
				exit(1);
			}
		}
	}
	else {
		for (i = 0, count = 0 ; count < number_of_passwords_to_generate ; i++) {
			rand = pseudo_random_function((unsigned char*)&i, sizeof(LONG_INDEX_PROJ), SEED);
			rand = rand % (rule.number_of_possible_passwords);
			generate_kth_password(rand,password,rule);
			cryptHash(hash_function,password,binary_hashed_passowrd);
			status = add_DEHT(deht,binary_hashed_passowrd,hash_func_size_in_bytes,(unsigned char*) password, strlen(password));
			if (status == DEHT_STATUS_FAIL) {
				free_lexicon(lexicon,length);
				free_rule(&rule);
				erase_DEHT_file(deht);
				exit(1);
			}
			count++;
		}
	}

	free_lexicon(lexicon,length);
	free_rule(&rule);
	lock_DEHT_files(deht);

	return 0;
}